package de.unikoblenz.west.csxpoi.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import de.unikoblenz.west.csxpoi.server.connector.*;

/**
 * Request object to forward queries to a given set of datastores and return the
 * results as a list of POIs
 * 
 * @author Yassin Nachite
 * 
 */
public abstract class Request {

	/**
	 * the constant representing the connection type http
	 */
	public static final int HTTP = 1;

	/**
	 * the constant representing the connection type sesame
	 */
	public static final int SESAME = 2;

	/**
	 * the constant representing the query type RDF (Sparql)
	 */
	public static final int RDF = 1;

	/**
	 * the constant representing a Restful query type
	 */
	public static final int REST = 2;

	/**
	 * List of connectors to the given datastores
	 */
	ArrayList<SourceConnector> connectors = null;

	/**
	 * List of data given datastores
	 */
	ArrayList<String> sources = null;

	/**
	 * Context of the servlet
	 */
	ServletContext context = null;

	protected Request(ServletContext context) {
		this.context = context;
		this.sources = getSources();
	}

	protected Request(File sources) {
		this.sources = getSources(sources);
	}

	/**
	 * Get datastores from the source definition file sources.xml
	 * 
	 * @return List of sources
	 */
	public ArrayList<String> getSources() {
		try {
			InputStream is = context
					.getResourceAsStream("/WEB-INF/sources.xml");
			return readSources(is);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * Get datastores from a given source definition file sources
	 * 
	 * @param sources
	 *            XML-File containing the sources
	 * @return List of sources
	 */
	public ArrayList<String> getSources(File sources) {

		try {
			InputStream is = new FileInputStream(sources);
			return readSources(is);
		} catch (Exception e) {
			return null;
		}

	}

	/**
	 * Reads datastores from an inputstream
	 * 
	 * @param is
	 *            Inputstream
	 * @return ArrayList of datastores
	 */
	public ArrayList<String> readSources(InputStream is) {
		ArrayList<String> sourceList = new ArrayList<String>();
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(is);
			doc.getDocumentElement().normalize();
			NodeList nodes = doc.getElementsByTagName("source");
			String repository = "", address = "", repo_id = "";

			for (int i = 0; i < nodes.getLength(); i++) {
				if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {

					NodeList children = ((Element) nodes.item(i))
							.getChildNodes();

					Element connectInfo = null;
					Element types = null;

					for (int k = 0; k < children.getLength(); k++) {
						if (children.item(k).getNodeType() == Node.ELEMENT_NODE) {
							if (children.item(k).getNodeName() == "connection") {
								connectInfo = (Element) children.item(k);
							}
							if (children.item(k).getNodeName() == "requestTypes")
								types = (Element) children.item(k);
						}
					}

					NodeList connectAttr = connectInfo.getChildNodes();
					NodeList typeDescr = types.getChildNodes();

					for (int j = 0; j < connectAttr.getLength(); j++) {
						if (connectAttr.item(j).getNodeType() == Node.ELEMENT_NODE) {
							Element child = (Element) connectAttr.item(j);

							if (child.getNodeName() == "repository") {
								try {
									repository = child.getFirstChild()
											.getTextContent();
								} catch (Exception e) {
									repository = " ";
								}
							} else if (child.getNodeName() == "adress") {
								try {
									address = child.getFirstChild()
											.getTextContent();
								} catch (Exception e) {
									address = " ";
								}
							} else if (child.getNodeName() == "repository_id") {
								try {
									repo_id = child.getFirstChild()
											.getTextContent();
								} catch (Exception e) {
									repo_id = " ";
								}
							}
						}

					}

					for (int j = 0; j < typeDescr.getLength(); j++) {
						if (typeDescr.item(j).getNodeType() == Node.ELEMENT_NODE) {
							Element child = (Element) typeDescr.item(j);
							if (child.getNodeName() == "type") {
								try {
									String myString = child.getFirstChild()
											.getTextContent();

									sourceList.add(myString + "," + repository
											+ "," + address + "," + repo_id);

//									System.out.println("Adress: " + myString + "," + repository + "," + address + "," + repo_id);
								} catch (Exception e) {
									System.out.println("Error retrieving source");
								}
							}
						}
					}

					repository = "";
					address = "";
					repo_id = "";

				}

			}
		} catch (Exception e) {
			e.printStackTrace();
			sourceList.clear();
			sourceList.add("sparql,sesame," + Constants.SESAME_SERVER + ","
					+ Constants.REPOSITORY_ID);
			sourceList.add("rest,sesame," + Constants.SESAME_SERVER + ","
					+ Constants.REPOSITORY_ID);
		}
		return sourceList;
	}

	abstract ArrayList<SourceConnector> initConnectors();

	public abstract List<String> getBases(String[] args);

	public abstract List<String> getContributions(String[] args);

	public abstract List<PoiWrapper> getPois(String[] args);

	public abstract List<String> getUsers(String[] args);

	public abstract List<String> getVocabulary(String[] args);

	public abstract List<CategoryWrapper> getCategory(String[] args);

}
